Niniejszy raport przedstawia kompleksową analizę danych dotyczących materiałów wykorzystywanych w technologii baterii, pochodzących z bazy Materials Project. Analiza objęła różnych materiałów, badając ich właściwości elektrochemiczne, strukturalne i energetyczne.
library(dplyr)
library(ggplot2)
library(gridExtra)
library(tidyr)
library(plotly)
library(caret)
Niniejszy raport przedstawia analizę bazy danych materiałów wykorzystywanych w tworzeniu baterii, pochodzących z projektu Materials Project. Materials Project to inicjatywa naukowa Departamentu Energii USA, której celem jest dostarczanie otwartych danych i narzędzi do analizy materiałów.
Wczytanie surowych danych z pliku CSV i wstępna analiza ich struktury.
batteries_raw_data <- read.csv("mp_batteries.csv")
batteries_raw_tbl <- as_tibble(batteries_raw_data)
str(batteries_raw_tbl)
## tibble [4,351 × 17] (S3: tbl_df/tbl/data.frame)
## $ Battery.ID : chr [1:4351] "mp-30_Al" "mp-1022721_Al" "mp-8637_Al" "mp-129_Al" ...
## $ Battery.Formula : chr [1:4351] "Al0-2Cu" "Al1-3Cu" "Al0-5Mo" "Al0-12Mo" ...
## $ Working.Ion : chr [1:4351] "Al" "Al" "Al" "Al" ...
## $ Formula.Charge : chr [1:4351] "Cu" "AlCu" "Mo" "Mo" ...
## $ Formula.Discharge : chr [1:4351] "Al2Cu" "Al3Cu" "Al5Mo" "Al12Mo" ...
## $ Max.Delta.Volume : num [1:4351] 3.04 1.24 4.76 12.72 12.49 ...
## $ Average.Voltage : num [1:4351] 0.089 -0.0216 0.1228 0.0431 0.0292 ...
## $ Gravimetric.Capacity : num [1:4351] 1368 1113 1742 2299 1901 ...
## $ Volumetric.Capacity : num [1:4351] 5563 4419 7176 7346 7333 ...
## $ Gravimetric.Energy : num [1:4351] 121.8 -24 213.8 99.1 55.6 ...
## $ Volumetric.Energy : num [1:4351] 495.3 -95.4 880.9 316.8 214.4 ...
## $ Atomic.Fraction.Charge : num [1:4351] 0 0.5 0 0 0 ...
## $ Atomic.Fraction.Discharge: num [1:4351] 0.667 0.75 0.833 0.923 0.923 ...
## $ Stability.Charge : num [1:4351] 0 0.0741 0.4115 0 0 ...
## $ Stability.Discharge : num [1:4351] 0 0.0962 0.0452 0.0114 0 ...
## $ Steps : int [1:4351] 1 1 1 1 1 1 1 1 1 1 ...
## $ Max.Voltage.Step : num [1:4351] 0 0 0 0 0 0 0 0 0 0 ...
head(batteries_raw_tbl)
## # A tibble: 6 × 17
## Battery.ID Battery.Formula Working.Ion Formula.Charge Formula.Discharge
## <chr> <chr> <chr> <chr> <chr>
## 1 mp-30_Al Al0-2Cu Al Cu Al2Cu
## 2 mp-1022721_Al Al1-3Cu Al AlCu Al3Cu
## 3 mp-8637_Al Al0-5Mo Al Mo Al5Mo
## 4 mp-129_Al Al0-12Mo Al Mo Al12Mo
## 5 mp-91_Al Al0-12W Al W Al12W
## 6 mp-1055908_Al Al0-12Mn Al Mn MnAl12
## # ℹ 12 more variables: Max.Delta.Volume <dbl>, Average.Voltage <dbl>,
## # Gravimetric.Capacity <dbl>, Volumetric.Capacity <dbl>,
## # Gravimetric.Energy <dbl>, Volumetric.Energy <dbl>,
## # Atomic.Fraction.Charge <dbl>, Atomic.Fraction.Discharge <dbl>,
## # Stability.Charge <dbl>, Stability.Discharge <dbl>, Steps <int>,
## # Max.Voltage.Step <dbl>
Sprawdzenie jakości danych pod kątem brakujących wartości, duplikatów oraz potencjalnych nieprawidłowości w danych.
missing_values <- colSums(is.na(batteries_raw_tbl))
missing_values_tbl <- tibble(
column = names(missing_values),
missing_count = missing_values
) %>%
filter(missing_count > 0)
if (nrow(missing_values_tbl) > 0) {
print("Kolumny z brakujacymi wartosciami:")
print(missing_values_tbl)
} else {
print("Brak brakujacych wartosci w zbiorze danych")
}
## [1] "Brak brakujacych wartosci w zbiorze danych"
duplicates_count <- sum(duplicated(batteries_raw_tbl))
print(paste("Liczba zduplikowanych wierszy:", duplicates_count))
## [1] "Liczba zduplikowanych wierszy: 0"
batteries_data_tbl <- batteries_raw_tbl %>%
rename_all(tolower) %>%
rename_all(~ gsub("\\.", "_", .)) %>%
mutate(
max_delta_volume = as.numeric(max_delta_volume),
average_voltage = as.numeric(average_voltage),
gravimetric_capacity = as.numeric(gravimetric_capacity),
volumetric_capacity = as.numeric(volumetric_capacity),
gravimetric_energy = as.numeric(gravimetric_energy),
volumetric_energy = as.numeric(volumetric_energy),
atomic_fraction_charge = as.numeric(atomic_fraction_charge),
atomic_fraction_discharge = as.numeric(atomic_fraction_discharge),
stability_charge = as.numeric(stability_charge),
stability_discharge = as.numeric(stability_discharge),
steps = as.integer(steps),
max_voltage_step = as.numeric(max_voltage_step)
)
str(batteries_data_tbl)
## tibble [4,351 × 17] (S3: tbl_df/tbl/data.frame)
## $ battery_id : chr [1:4351] "mp-30_Al" "mp-1022721_Al" "mp-8637_Al" "mp-129_Al" ...
## $ battery_formula : chr [1:4351] "Al0-2Cu" "Al1-3Cu" "Al0-5Mo" "Al0-12Mo" ...
## $ working_ion : chr [1:4351] "Al" "Al" "Al" "Al" ...
## $ formula_charge : chr [1:4351] "Cu" "AlCu" "Mo" "Mo" ...
## $ formula_discharge : chr [1:4351] "Al2Cu" "Al3Cu" "Al5Mo" "Al12Mo" ...
## $ max_delta_volume : num [1:4351] 3.04 1.24 4.76 12.72 12.49 ...
## $ average_voltage : num [1:4351] 0.089 -0.0216 0.1228 0.0431 0.0292 ...
## $ gravimetric_capacity : num [1:4351] 1368 1113 1742 2299 1901 ...
## $ volumetric_capacity : num [1:4351] 5563 4419 7176 7346 7333 ...
## $ gravimetric_energy : num [1:4351] 121.8 -24 213.8 99.1 55.6 ...
## $ volumetric_energy : num [1:4351] 495.3 -95.4 880.9 316.8 214.4 ...
## $ atomic_fraction_charge : num [1:4351] 0 0.5 0 0 0 ...
## $ atomic_fraction_discharge: num [1:4351] 0.667 0.75 0.833 0.923 0.923 ...
## $ stability_charge : num [1:4351] 0 0.0741 0.4115 0 0 ...
## $ stability_discharge : num [1:4351] 0 0.0962 0.0452 0.0114 0 ...
## $ steps : int [1:4351] 1 1 1 1 1 1 1 1 1 1 ...
## $ max_voltage_step : num [1:4351] 0 0 0 0 0 0 0 0 0 0 ...
write.csv(batteries_data_tbl, "mp_batteries_clean.csv", row.names = FALSE)
Poniżej przedstawiono opis wszystkich zmiennych w wyczyszczonym zbiorze danych:
W tej sekcji przedstawiono interaktywny wykres rozkładów wszystkich zmiennych numerycznych w naszym zbiorze danych. Wykres umożliwia wybór zmiennej z rozwijanej listy, co pozwala na szczegółową analizę rozkładu każdej z nich. Zastosowano histogram z możliwością powiększania i przesuwania dla lepszej wizualizacji szczegółów rozkładu.
numeric_vars <- c(
"max_delta_volume", "average_voltage", "gravimetric_capacity",
"volumetric_capacity", "gravimetric_energy", "volumetric_energy",
"atomic_fraction_charge", "atomic_fraction_discharge",
"stability_charge", "stability_discharge",
"steps", "max_voltage_step"
)
plot_data <- batteries_data_tbl %>%
select(numeric_vars)
plot_ly(
x = plot_data$volumetric_capacity,
type = "histogram",
marker = list(
color = "lightblue",
line = list(
color = "black",
width = 1
)
),
hovertemplate = paste(
"Wartosc: %{x}<br>",
"Liczba obserwacji: %{y}<br>",
"<extra></extra>"
)
) %>%
layout(
updatemenus = list(
list(
type = "dropdown",
y = 0.9,
x = 0.9,
buttons = lapply(numeric_vars, function(var) {
list(
method = "restyle",
args = list(
list(
x = list(plot_data[[var]]),
name = var
)
),
label = var
)
})
)
),
xaxis = list(title = "Wartosc"),
yaxis = list(title = "Liczba obserwacji"),
title = c(
text = "Rozklad zmiennych numerycznych"
),
margin = c(
t = 100,
b = 50,
l = 50,
r = 50
),
showlegend = FALSE
)
W tej części analizujemy częstość występowania poszczególnych jonów roboczych w badanych materiałach bateryjnych. Wykres słupkowy przedstawia liczbę materiałów wykorzystujących poszczególne jony jako nośniki ładunku.
batteries_data_tbl %>%
count(working_ion, sort = TRUE) %>%
ggplot(aes(x = reorder(working_ion, n), y = n)) +
geom_bar(stat = "identity", fill = "lightblue", color = "black") +
coord_flip() +
labs(x = "Jon roboczy", y = "Liczba materialow")
Dominującym jonem roboczym jest lit (Li), występujący w większości badanych materiałów bateryjnych. Pozostałe jony, takie jak Na (sód), Mg (magnez) czy Ca (wapń), stanowią mniejszą część.
Poniższa analiza przedstawia, jak często występują różne kombinacje wzorów chemicznych w stanie naładowanym i rozładowanym, co pozwoli nam zidentyfikować dominujące wzorce w projektowaniu materiałów bateryjnych. Ze względu na dużą liczbę unikalnych kombinacji, przedstawiono tylko najczęściej występujące przypadki.
batteries_data_tbl %>%
count(formula_charge, formula_discharge, sort = TRUE) %>%
head(10) %>%
mutate(
pair_label = paste(formula_charge, "-", formula_discharge),
) %>%
ggplot(aes(x = reorder(pair_label, n), y = n)) +
geom_bar(stat = "identity", fill = "lightblue", color = "black") +
coord_flip() +
labs(
x = "Para wzorów (naladowany - rozladowany)",
y = "Liczba wystapien"
)
Występują powtarzające się wzorce przejścia między stanami naładowania i rozładowania. Dodatkowo materiały w stanie rozładowanym są bardziej złożone chemicznie.
W tej części przedstawiono podstawowe statystyki opisowe dla wszystkich zmiennych numerycznych w zbiorze danych. Dla każdej zmiennej obliczono wartości centralne (średnia, mediana), miary rozproszenia (odchylenie standardowe, rozstęp) oraz kwartyle.
batteries_data_tbl %>%
select(numeric_vars) %>%
summarise(across(everything(), list(
min = ~ min(.),
max = ~ max(.),
q1 = ~ quantile(., 0.25),
median = ~ median(.),
mean = ~ mean(.),
q3 = ~ quantile(., 0.75),
sd = ~ sd(.)
))) %>%
pivot_longer(everything(),
names_to = c("variable", "statistic"),
names_sep = "_(?=[^_]+$)"
) %>%
pivot_wider(names_from = statistic, values_from = value) %>%
knitr::kable(
caption = "Statystyki opisowe dla zmiennych numerycznych",
digits = 3
)
| variable | min | max | q1 | median | mean | q3 | sd |
|---|---|---|---|---|---|---|---|
| max_delta_volume | 0.000 | 293.193 | 0.017 | 0.042 | 0.375 | 0.086 | 6.852 |
| average_voltage | -7.755 | 54.569 | 2.226 | 3.301 | 3.083 | 4.019 | 1.822 |
| gravimetric_capacity | 5.177 | 2557.627 | 88.108 | 130.691 | 158.291 | 187.600 | 164.914 |
| volumetric_capacity | 24.079 | 7619.191 | 311.615 | 507.031 | 610.624 | 722.755 | 563.853 |
| gravimetric_energy | -583.546 | 5926.950 | 211.692 | 401.788 | 444.106 | 614.416 | 351.048 |
| volumetric_energy | -2208.075 | 18305.898 | 821.625 | 1463.788 | 1664.048 | 2252.257 | 1297.799 |
| atomic_fraction_charge | 0.000 | 0.909 | 0.000 | 0.000 | 0.040 | 0.048 | 0.089 |
| atomic_fraction_discharge | 0.007 | 0.993 | 0.087 | 0.143 | 0.159 | 0.200 | 0.120 |
| stability_charge | 0.000 | 6.487 | 0.033 | 0.073 | 0.143 | 0.132 | 0.378 |
| stability_discharge | 0.000 | 6.278 | 0.020 | 0.049 | 0.122 | 0.093 | 0.352 |
| steps | 1.000 | 6.000 | 1.000 | 1.000 | 1.167 | 1.000 | 0.464 |
| max_voltage_step | 0.000 | 26.961 | 0.000 | 0.000 | 0.150 | 0.000 | 0.630 |
Większość zmiennych energetycznych (gravimetric_energy, volumetric_energy, gravimetric_capacity) wykazuje rozkład prawoskośny
W tej części przeprowadzimy analizę korelacji między zmiennymi numerycznymi w naszym zbiorze danych. Pozwoli to na identyfikację istotnych zależności między różnymi właściwościami materiałów bateryjnych.
correlation_matrix <- batteries_data_tbl %>%
select(numeric_vars) %>%
cor()
correlation_long <- correlation_matrix %>%
as.data.frame() %>%
mutate(variable1 = colnames(correlation_matrix)) %>%
pivot_longer(-variable1,
names_to = "variable2",
values_to = "correlation"
)
ggplot(
correlation_long,
aes(x = variable1, y = variable2, fill = correlation)
) +
geom_tile() +
scale_fill_gradient2(
low = "blue", mid = "white", high = "red",
midpoint = 0, limits = c(-1, 1)
) +
geom_text(aes(label = sprintf("%.2f", correlation)), size = 3) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
) +
labs(x = "Zmienna 1", y = "Zmienna 2", fill = "Korelacja")
W poniższej tabeli przedstawiono 10 najsilniejszych korelacji między zmiennymi w zbiorze danych, uporządkowanych według wartości bezwzględnej współczynnika korelacji. Pozwala to na szybką identyfikację najistotniejszych zależności między właściwościami materiałów bateryjnych.
top_correlations <- correlation_long %>%
filter(variable1 < variable2) %>%
arrange(desc(abs(correlation))) %>%
head(10)
knitr::kable(
top_correlations %>%
mutate(correlation = round(correlation, 3)) %>%
rename(
"Zmienna 1" = variable1,
"Zmienna 2" = variable2,
"Korelacja" = correlation
),
caption = "10 najsilniejszych korelacji miedzy zmiennymi"
)
| Zmienna 1 | Zmienna 2 | Korelacja |
|---|---|---|
| gravimetric_energy | volumetric_energy | 0.928 |
| gravimetric_capacity | volumetric_capacity | 0.858 |
| stability_charge | stability_discharge | 0.803 |
| atomic_fraction_discharge | gravimetric_capacity | 0.681 |
| average_voltage | gravimetric_energy | 0.666 |
| atomic_fraction_discharge | volumetric_capacity | 0.618 |
| atomic_fraction_charge | atomic_fraction_discharge | 0.597 |
| average_voltage | volumetric_energy | 0.555 |
| max_voltage_step | steps | 0.535 |
| gravimetric_capacity | max_delta_volume | 0.434 |
Poniżej przedstawiono wykresy rozrzutu dla trzech najsilniejszych korelacji między zmiennymi. Wykresy te pozwalają na dokładniejszą analizę charakteru zależności między wybranymi parami zmiennych.
top_3_correlations <- top_correlations %>%
head(3)
create_scatter_plot <- function(data, var1, var2) {
ggplot(data, aes(x = .data[[var1]], y = .data[[var2]])) +
geom_point(color = "blue") +
geom_smooth(method = "lm", color = "red") +
theme_minimal() +
labs(
title = paste("Korelacja:", var1, "vs", var2),
x = var1,
y = var2
)
}
for (i in 1:3) {
print(create_scatter_plot(
batteries_data_tbl,
top_3_correlations$variable1[i],
top_3_correlations$variable2[i]
))
}
W tej sekcji przedstawiono interaktywną analizę porównawczą różnych jonów roboczych wykorzystywanych w materiałach bateryjnych. Wykres radarowy umożliwia porównanie czterech kluczowych charakterystyk:
Wykres jest interaktywny - można włączać i wyłączać poszczególne jony za pomocą legendy, co ułatwia porównanie wybranych par lub grup jonów.
ion_performance <- batteries_data_tbl %>%
group_by(working_ion) %>%
filter(n() > 5) %>%
summarise(
avg_voltage = mean(average_voltage),
energy_density = mean(gravimetric_energy),
stability_score = mean((stability_charge + stability_discharge) / 2),
volume_change = mean(max_delta_volume)
)
radar_data <- ion_performance %>%
mutate(across(-working_ion, ~ scale(.) %>% as.vector())) %>%
pivot_longer(-working_ion, names_to = "metric", values_to = "value") %>%
group_by(working_ion) %>%
group_modify(~ bind_rows(.x, .x[1, ]))
radar_plot <- plot_ly(
type = "scatterpolar",
mode = "lines+markers"
)
for (ion in unique(radar_data$working_ion)) {
ion_data <- radar_data %>% filter(working_ion == ion)
radar_plot <- radar_plot %>%
add_trace(
r = ion_data$value,
theta = ion_data$metric,
name = ion,
fill = "toself",
visible = if (ion == "Li") TRUE else "legendonly",
hoverinfo = "text",
text = paste0(
"Jon: ", ion, "<br>",
"Metryka: ", ion_data$metric, "<br>",
"Wartosc: ", round(ion_data$value, 2)
)
)
}
radar_plot %>%
layout(
title = c(
text = "Porownanie charakterystyk roznych jonow roboczych "
),
margin = c(
t = 100,
b = 50,
l = 50,
r = 50
),
polar = list(
radialaxis = list(
visible = TRUE,
range = c(min(radar_data$value), max(radar_data$value)),
showticklabels = FALSE
)
),
showlegend = TRUE
)
W tej części przygotujemy dane do stworzenia modelu predykcyjnego dla
stabilności materiału w stanie naładowanym
(stability_charge). Model ten może być szczególnie
użyteczny przy projektowaniu nowych materiałów bateryjnych, pozwalając
przewidzieć ich stabilność na podstawie innych właściwości.
Proces przygotowania danych obejmuje:
battery_id,
battery_formula)stability_discharge ze względu na
wysoką korelację ze zmienną przewidywanąworking_ion na
zmienne binarne (dummy variables)model_data <- batteries_data_tbl %>%
select(-battery_id, -battery_formula, -stability_discharge) %>%
mutate(
charge_elements_count = stringr::str_count(formula_charge, "[A-Z]"),
discharge_elements_count = stringr::str_count(formula_discharge, "[A-Z]"),
working_ion = as.factor(working_ion)
) %>%
select(-formula_charge, -formula_discharge) %>%
fastDummies::dummy_cols("working_ion", remove_selected_columns = TRUE)
train_index <- createDataPartition(
model_data$stability_charge,
p = 0.8,
list = FALSE
)
train_data <- model_data[train_index, ]
test_data <- model_data[-train_index, ]
ggplot() +
geom_density(
data = train_data,
aes(x = stability_charge, fill = "Treningowy"),
alpha = 0.7
) +
geom_density(
data = test_data,
aes(x = stability_charge, fill = "Testowy"),
alpha = 0.7
) +
scale_fill_manual(values = c(
"Treningowy" = "lightblue", "Testowy" = "lightgreen"
)) +
theme_minimal() +
labs(
title = "Rozklad zmiennej zależnej w zbiorach treningowym i testowym",
x = "Wskaznik stabilnosci",
y = "Gestosc"
)
W tej części wykorzystamy algorytm Random Forest do przewidywania stabilności materiału w stanie naładowanym. Model wykorzystuje technikę walidacji krzyżowej (5-krotna z 3 powtórzeniami) do oceny swojej skuteczności. Model jest zapisywany po treningu, co pozwala uniknąć ponownego trenowania przy każdej generacji raportu.
model_file <- "model.rds"
if (file.exists(model_file)) {
model <- readRDS(model_file)
} else {
train_control <- trainControl(
method = "repeatedcv",
number = 5,
repeats = 3,
savePredictions = "final",
verboseIter = TRUE
)
model <- train(
stability_charge ~ .,
data = train_data,
method = "rf",
trControl = train_control,
metric = "RMSE",
tuneLength = 5,
verbose = TRUE
)
saveRDS(model, model_file)
}
print(model)
## Random Forest
##
## 3483 samples
## 22 predictor
##
## No pre-processing
## Resampling: Cross-Validated (5 fold, repeated 3 times)
## Summary of sample sizes: 2786, 2786, 2788, 2785, 2787, 2787, ...
## Resampling results across tuning parameters:
##
## mtry RMSE Rsquared MAE
## 2 0.3279814 0.3134437 0.10625915
## 7 0.3183121 0.3305744 0.09962478
## 12 0.3192961 0.3277467 0.09952867
## 17 0.3193430 0.3266018 0.09879872
## 22 0.3215766 0.3225087 0.09898386
##
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was mtry = 7.
Poniżej przedstawiono wyniki modelu wraz z wizualizacjami pokazującymi jego skuteczność:
predictions <- predict(model, test_data)
model_metrics <- postResample(predictions, test_data$stability_charge)
print("Metryki modelu na zbiorze testowym:")
## [1] "Metryki modelu na zbiorze testowym:"
print(model_metrics)
## RMSE Rsquared MAE
## 0.17096324 0.86087882 0.06063573
prediction_plot <- ggplot() +
geom_point(
aes(x = test_data$stability_charge, y = predictions),
alpha = 0.5,
color = "blue"
) +
geom_abline(intercept = 0, slope = 1, color = "red") +
theme_minimal() +
labs(
title = "Porownanie wartosci przewidzianych i rzeczywistych",
x = "Rzeczywisty wskaznik stabilnosci",
y = "Przewidziany wskaznik stabilnosci"
)
print(prediction_plot)
importance <- varImp(model)
importance_plot <- ggplot(importance) +
geom_bar(stat = "identity", fill = "lightblue", color = "black") +
labs(
title = "Waznosc zmiennych w modelu",
x = "Zmienne",
y = "Wzgledna waznosc [%]"
)
print(importance_plot)